#include "stdafx.h"
#include <fstream>
#include <GL/glew.h>
#include <GL/glut.h>

using namespace std;

#include "shaders.h"

#include "point3d.h"
#include "vector3d.h"
#include "util.h"
#include "object3d.h"

// width and height of the window.
int Glut_w = 1280, Glut_h = 1024; 
double flov = 90.0;

//Variables and their values for the camera setup.
myPoint3D camera_eye(0,0,5);
myVector3D camera_up(0,1,0);
myVector3D camera_forward (0,0,-1);

int button_pressed = 0; // 1 if a button is currently being pressed.
int GLUTmouse[2] = { 0, 0 };


myObject3D *myobj1;


//Data representing a cube with 8 vertices, 6 faces, and each vertex has red color.
GLuint buffers[3];
/*GLfloat vertices[][3] = {
	{1, 1, 1},
	{1, 1, -1},
	{1,-1,1},
	{1,-1,-1},
	{-1,1,1},
	{-1,1,-1},
	{-1,-1,1},
	{-1,-1,-1}
};

GLuint indices[][4] = {
	{0,1,3,2},
	{4,5,7,6},

	{0,2,6,4},
	{1,3,7,5}, 
	
	{0,1,5,4},
	{2,3,7,6}
};

GLfloat colors[][3] = {
	{1,0,0},
	{1,0,0},
	{1,0,0},
	{1,0,0},
	{1,0,0},
	{1,0,0},
	{1,0,0},
	{1,0,0}
};*/


//This function is called when a mouse button is pressed.
void mouse(int button, int state, int x, int y)
{
  // Remember button state 
  button_pressed = (state == GLUT_DOWN ) ? 1 : 0;

   // Remember mouse position 
  GLUTmouse[0] = x;
  GLUTmouse[1] = Glut_h - y;
}

//This function is called when the mouse is dragged.
void mousedrag(int x, int y)
{
  // Invert y coordinate
  y = Glut_h - y;

  //change in the mouse position since last time
  int dx = x - GLUTmouse[0];
  int dy = y - GLUTmouse[1];

  GLUTmouse[0] = x;
  GLUTmouse[1] = y;

  if (dx == 0 && dy == 0) return;
  if (button_pressed == 0) return;

  /**ADD CODE TO ROTATE THE CAMERA AROUND THE CURRENT POSITION**/
  myVector3D rightVector;
  crossproduct(camera_up,camera_forward,rightVector);
  myVector3D deplacementVector = (rightVector*dx) + (camera_up*-dy);
  myVector3D rotateAxis;
  crossproduct(deplacementVector,camera_forward,rotateAxis);
  rotate2(camera_forward,rotateAxis,0.005);
//rotate2(camera_eye,rotateAxis,0.005);
//rotate2(camera_up,rotateAxis,0.005);

  glutPostRedisplay();
}

//This function is called when a key is pressed.
void keyboard(unsigned char key, int x, int y) {
	switch(key) {
	case 27:  // Escape to quit
		exit(0) ;
        break ;
	}
	glutPostRedisplay();
}

//This function is called when an arrow key is pressed.
void keyboard2(int key, int x, int y) {
	switch(key) {
	case GLUT_KEY_UP:
		camera_eye.Z--;
		break;
	case GLUT_KEY_DOWN:
		camera_eye.Z++;
		break;
	case GLUT_KEY_LEFT:
		camera_eye.X--;
		break;
	case GLUT_KEY_RIGHT:
		camera_eye.X++;
		break;
	}
	glutPostRedisplay();
}

//This function is called to display objects on screen.
void display() 
{
	//Clearing the color on the screen.
	glClearColor(0.0, 0, 0, 0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	//Setting up the projection matrix.
    glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(flov, Glut_w/(float)Glut_h, 0.1, 99);
	glViewport(0, 0, Glut_w, Glut_h);

	//Setting up the modelview matrix.
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(camera_eye.X, camera_eye.Y, camera_eye.Z, camera_eye.X + camera_forward.dX, camera_eye.Y + camera_forward.dY, camera_eye.Z + camera_forward.dZ, camera_up.dX, camera_up.dY, camera_up.dZ);

	//Code to draw the cube stored in vertices, colors, indices data.
	/*glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
    glVertexPointer(3, GL_FLOAT, 0, 0);
    glEnableClientState(GL_VERTEX_ARRAY);

    glBindBuffer(GL_ARRAY_BUFFER, buffers[1]);
    glColorPointer(3, GL_FLOAT, 0, 0) ; 
    glEnableClientState(GL_COLOR_ARRAY) ; 

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[2]);

    glDrawElements(GL_QUADS, 6*4, GL_UNSIGNED_INT, 0) ; */

	/**If you have written functions in myObject3D correctly, the following
	   code will draw the object and its normals. Right now it does nothing. **/
	myobj1->displayObject();
	myobj1->displayNormals();

	glFlush();
}

//This function is called from the main to initalize everything.
void init()
{
	//Initializing the buffer objects for the cube.
	/*glGenBuffers(3, buffers);
	
	glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
    glBufferData(GL_ARRAY_BUFFER, 8*3*4, vertices, GL_STATIC_DRAW);

	glBindBuffer(GL_ARRAY_BUFFER, buffers[1]) ; 
    glBufferData(GL_ARRAY_BUFFER, 8*3*4, colors, GL_STATIC_DRAW);
 
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[2]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, 6*4*4, indices, GL_STATIC_DRAW);*/

	/**If you have written functions in myObject3D correctly, the following
		code will read a mesh, compute normals, and initialize buffer objects.
		Right now it does nothing.**/
	myobj1 = new myObject3D();
	myobj1->readMesh("cube.obj");
	myobj1->computeNormals();
	myobj1->createObjectBuffers();
}


int main(int argc, char* argv[]) {
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA | GLUT_DEPTH);
	glutCreateWindow("My OpenGL Application");
	   
	glewInit() ; 
	glutReshapeWindow(Glut_w, Glut_h);
	
	glutDisplayFunc(display);
	glutKeyboardFunc(keyboard);
	glutSpecialFunc(keyboard2);
	glutMotionFunc(mousedrag) ;
	glutMouseFunc(mouse) ;

	glEnable(GL_DEPTH_TEST);
	glDepthFunc (GL_LESS) ;

	init();

	glutMainLoop();
	return 0;
}